//////////////////////////////////////
// Fast Container Library           //
// MIT License - Copyright (c) 2012 //
//////////////////////////////////////

#pragma once

#include "common.h"
#include "algobase.h"

#include <cmath>

// standard math defines
#define FC_E        2.718281828459f
#define FC_LOG2E    1.442695040888f
#define FC_LOG10E   0.434294481903f
#define FC_LN2      0.693147180559f
#define FC_LN10     2.302585092994f
#define FC_PI       3.141592653589f
#define FC_TWOPI    6.283185307179f
#define FC_PI_2     1.570796326794f
#define FC_PI_4     0.785398163397f
#define FC_1_PI     0.318309886183f
#define FC_2_PI     0.636619772367f
#define FC_2_SQRTPI 1.128379167095f
#define FC_SQRT2    1.414213562373f
#define FC_SQRT1_2  0.707106781186f


FC_NAMESPACE_BEGIN


inline float to_degrees( float radians ) { return (radians * 57.295779513f ); }
inline float to_radians( float degrees ) { return (degrees * 0.0174532925f); }

inline float wrap_radians( float x )
	{
		if( x < -FC_PI ) x += FC_TWOPI;
		else if( x > FC_PI ) x -= FC_TWOPI;
		return x;
	}

template <class Integral> inline 
	bool is_odd( Integral x )
	{
		return ((x & Integral(1)) == Integral(1));
	}

template <class Integral> inline 
	bool is_even( Integral x )
	{
		return ((x & Integral(1)) == Integral(0));
	}

template <class T> inline 
	T abs( T x )
	{
		return x < T(0) ? -x : x;
	}

inline float log2( float x )
	{
		return logf(x) / 0.69314718f;
	}

inline float round( float x )
	{
		return (float)int((x > 0.f) ? (x + 0.5f) : (x - 0.5f));
	}

inline int iround( float x )
	{
		return int((x > 0.f) ? (x + 0.5f) : (x - 0.5f));
	}

template<class T> inline
	void minmax( const T& a, const T& b, T& outMin, T& outMax )
	{
		if( a < b )
		{
			outMin = a;
			outMax = b;
		}
		else
		{
			outMin = b;
			outMax = a;
		}
	}

template<class T> inline
	const T& min( const T& a, const T& b, const T& c )
	{
		return min( a, min(b, c) );
	}

template<class T> inline
	const T& max( const T& a, const T& b, const T& c )
	{
		return max( a, max(b, c) );
	}

template<class T> inline
	const T& min( const T& a, const T& b, const T& c, const T& d )
	{
		return min( a, min(b, min(c, d)) );
	}

template<class T> inline
	const T& max( const T& a, const T& b, const T& c, const T& d )
	{
		return max( a, max(b, max(c, d)) );
	}

inline float lerp( float x, float y, float t )
	{
		return x + (y - x) * t;
	}

inline float smooth_step( float x, float y, float t )
	{
		t = (t * t) * (3.f - (2.f * t));
		return x + (y - x) * t;
	}

inline void sincos( const float x, float& s, float& c )
{
	#if defined(_MSC_VER) || defined(__ICC)
	__asm
	{
		fld		x
		fsincos
		mov		eax, s
		mov		ecx, c
		fstp	dword ptr [ecx]
		fstp	dword ptr [eax]
	};
	#elif defined(__GNUC__)
	__asm__
	(
		"fsincos" :
		"=t" ( c ) ,
		"=u" ( s ) :
		"0"  ( x )
	);
	#else
		s = sinf( x );
		c = cosf( x );
	#endif
}

inline void dsincos( const float degrees, float& s, float& c )
	{
		sincos( to_radians(degrees), s, c );
	}


/*
//  fast sin, cos, atan functions taken from glm.lib
//                (glm@g-truc.net)
//
//
// Copyright (c) 2005 - 2012 G-Truc Creation (www.g-truc.net)
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// 
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
*/

inline float fast_sin( const float x )
	{
		return x - ((x * x * x) / 6.f) + ((x * x * x * x * x) / 120.f) -
			((x * x * x * x * x * x * x) / 5040.f);
	}

inline float fast_cos( const float x )
	{
		return 1.f - (x * x * 0.5f) + (x * x * x * x * 0.0416666666f) -
			(x * x * x * x * x * x * 0.0013888888f);
	}

inline void fast_sincos( const float x, float & s, float & c )
	{
		s = x - ((x * x * x) / 6.f) + ((x * x * x * x * x) / 120.f) - ((x * x * x * x * x * x * x) / 5040.f);
		c = 1.f - (x * x * 0.5f) + (x * x * x * x * 0.0416666666f) - (x * x * x * x * x * x * 0.0013888888f);
	}

inline float fast_dsin( const float degrees )
	{
		return fast_sin( to_radians(degrees) );
	}

inline float fast_dcos( const float degrees )
	{
		return fast_cos( to_radians(degrees) );
	}

inline void fast_dsincos( const float degrees, float& s, float& c )
	{
		fast_sincos( to_radians(degrees), s, c );
	}

inline float fast_atan( const float x )
	{
		return x - (x * x * x * 0.33333333f) + (x * x * x * x * x * 0.2f) - 
			(x * x * x * x * x * x * x * 0.1428571429f) + (x * x * x * x * x * x * x * x * x * 0.11111111111f) - 
			(x * x * x * x * x * x * x * x * x * x * x * 0.09090909f);
	}

inline float sqrt( const float x )  
	{
		return ::sqrtf(x);
	}

inline float fast_sqrt( const float x )  
	{
		union {
			int i;
			float x;
		} u;

		u.x = x;
		u.i = (1 << 29) + (u.i >> 1) - (1 << 22); 

		return u.x;
	}

inline float fast_atan2( const float y, const float x )
	{
		if ( x == 0.0f )
		{
			if ( y > 0.f ) return FC_PI_2;
			if ( y == 0.f ) return 0.f;
			return -FC_PI_2;
		}
		float atan;
		const float z = y / x;
		if ( fabsf( z ) < 1.f )
		{
			atan = z / ( 1.f + 0.28f * z * z );
			if ( x < 0.f )
			{
				if ( y < 0.f ) return atan - FC_PI;
				return atan + FC_PI;
			}
		}
		else
		{
			atan = FC_PI_2 - z / ( z * z + 0.28f );
			if ( y < 0.f ) return atan - FC_PI;
		}

		return atan;
	}

inline float atan2( float y, float x )
	{
		if( x > 0.f )
		{
			if( y == 0.f ) return 0.f;
			return fast_atan(y/x);
		}
		else if( x < 0.f )
		{
			if( y > 0.f ) return FC_PI_2 + fast_atan(-x/y);
			if( y < 0.f ) return -FC_PI_2 - fast_atan(x/y);
			return FC_PI;
		}

		//else x equals 0/inf/nan
		if( y > 0.f ) return FC_PI_2;
		if( y < 0.f ) return -FC_PI_2;

		return 0.f;
	}

inline float distance( float x1, float y1, float x2, float y2 ) 
	{
		float a = x1 - x2;
		float b = y1 - y2;
		return fc::sqrt( a * a + b * b );
	}

inline float fast_distance( float x1, float y1, float x2, float y2 ) 
	{
		float a = x1 - x2;
		float b = y1 - y2;
		return fc::fast_sqrt( a * a + b * b );
	}

inline float distance_squared( float x1, float y1, float x2, float y2 ) 
	{
		float a = x1 - x2;
		float b = y1 - y2;
		return a * a + b * b;
	}





FC_NAMESPACE_END


